package pro.seac.twitter.web;

import static org.apache.commons.lang3.StringUtils.EMPTY;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;

import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.ConfigAttribute;
import org.springframework.security.access.SecurityConfig;
import org.springframework.security.access.annotation.Secured;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;

import com.vaadin.navigator.Navigator;
import com.vaadin.navigator.View;
import com.vaadin.navigator.ViewDisplay;
import com.vaadin.navigator.ViewProvider;

public class ApplicationNavigator extends Navigator implements ViewProvider {
	private static final long serialVersionUID = 1L;
	private ApplicationContext ctx;
	public final static String ALL_REQUESTS = "**";

	private AccessDecisionManager accessDecisionManager;

	public ApplicationNavigator(ApplicationUI applicationUI, ApplicationContext ctx) {
		super(applicationUI, (ViewDisplay) applicationUI);
		this.ctx = ctx;
		accessDecisionManager = ctx.getBean(AccessDecisionManager.class);
		addProvider(this);
	}

	public String getViewName(String viewAndParameters) {
		return viewAndParameters;
	}

	public View getView(String uri) {
		View view = (View) resolveNavigateableByUri(uri);
		return view;
	}

	private Object resolveNavigateableByUri(String uri) {

		Set<String> beanNames = new LinkedHashSet<String>(ctx.getBeanDefinitionCount());
		beanNames.addAll(Arrays.asList(ctx.getBeanDefinitionNames()));

		for (String beanName : beanNames) {
			Navigateable navigateable = ctx.findAnnotationOnBean(beanName, Navigateable.class);
			if (navigateable != null) {
				String beanUri = navigateable.value();
				if (beanUri.endsWith(ALL_REQUESTS) && uri.startsWith(StringUtils.replace(beanUri, ALL_REQUESTS, EMPTY))) {
					checkNavigateableAccess(beanName);
					return ctx.getBean(beanName);
				} else if (StringUtils.equals(uri, beanUri)) {
					checkNavigateableAccess(beanName);
					return ctx.getBean(beanName);
				}
			}
		}
		return null;
	}

	protected void checkNavigateableAccess(String beanName) {
		Secured secured = ctx.findAnnotationOnBean(beanName, Secured.class);
		if (secured != null) {
			String[] roles = secured.value();
			List<ConfigAttribute> attributes = new ArrayList<ConfigAttribute>(roles.length);
			for (String role : roles) {
				attributes.add(new SecurityConfig(role));
			}
			Authentication auth = SecurityContextHolder.getContext().getAuthentication();
			if (auth == null) {
				throw new AccessDeniedException("Authentication==null");
			} else {
				accessDecisionManager.decide(auth, null, attributes);
			}
		}
	}
}
